package application

import (
	log "github.com/cihub/seelog"
	"os"
	"time"
	"tunnel/common/config"
	"tunnel/common/config/protocol"
	"tunnel/common/config/role"
	"tunnel/tunnel"
	"tunnel/tunnel/tcptunnel"
)

//
// Application
// @Description:
//
type Application struct {
	Config   config.Config
	Role     role.Name
	Protocol protocol.Name
}

//
// New
// @Description:
// @return *Application
//
func New() *Application {
	return &Application{
		Config:   config.Current,
		Role:     config.Current.Global.Role,
		Protocol: config.Current.Global.Protocol,
	}
}

//
// Service
// @Description:
//
type Service interface {
	//
	// Init
	// @Description: run once before start service
	// @return error
	//
	Init() error
	//
	// Start
	// @Description: start service
	// @return error
	//
	Start() error
	//
	// Stop
	// @Description: stop service
	//
	Stop()
}

//
// Run
// @Description: run application
// @receiver app
//
func (app *Application) Run() {
	log.Info("tunnel version : ", Version, " - ", Date)
	app.PProf()
	var serv Service
	var restart bool
	switch app.Role {
	case role.Server:
		serv = app.serverService()
		if app.Config.Global.Restart {
			log.Info("global.restart is not available in server application")
			app.Config.Global.Restart = false
		}
	case role.Client:
		serv = app.clientService()
		restart = app.Config.Global.Restart
	}
	if serv == nil {
		_ = log.Warn("tunnel server type not support")
		os.Exit(-1)
		return
	}
	ch := make(chan error, 1)
	if err := serv.Init(); err != nil {
		_ = log.Warn("init failed : ", err)
		os.Exit(-1)
		return
	} else {
		log.Info("service init success...")
	}
	go func() {
		ch <- serv.Start()
	}()
	for {
		select {
		case err := <-ch:
			if err != nil {
				serv.Stop()
				_ = log.Warn("tunnel exit with error : ", err.Error())
				if _, ok := tunnel.AllowRestart[err]; ok && restart {
					log.Info("tunnel restart in 10s...")
					time.Sleep(time.Second * 10)
					ch <- serv.Start()
				} else {
					os.Exit(-1)
					return
				}
			} else {
				log.Info("tunnel exited")
				os.Exit(0)
				return
			}
		}
	}
}

//
// clientService
// @Description:
// @receiver app
// @return Service
//
func (app *Application) clientService() Service {
	switch app.Protocol {
	case protocol.TCP:
		return tcptunnel.NewClientV3()
	}
	return nil
}

//
// serverService
// @Description:
// @receiver app
// @return Service
//
func (app *Application) serverService() Service {
	switch app.Protocol {
	case protocol.TCP:
		return tcptunnel.NewServerV3()
	}
	return nil
}
